#include "AlmacenamientoDocFrec.h"

int AlmacenamientoDocFrec::codigoArchivo = 0;
int AlmacenamientoDocFrec::codigoArchivoNivel1 = 0;

// CONSTRUCTOR
AlmacenamientoDocFrec::AlmacenamientoDocFrec(char * modo, int nivel, bool parcial) {
	
	string nombre_archivo;
	stringstream cod_str;
	
	nombre_archivo = Entorno::Instance().getDataPath().c_str();
	nombre_archivo += NOMBRE_ARCHIVO_DOCFREC;
	
	destroy = false;
	
	if (parcial) {
		if (nivel == 0) {
			AlmacenamientoDocFrec::codigoArchivo++;
			nombre_archivo += "_N0_";
			cod_str << codigoArchivo;
		} else {
			AlmacenamientoDocFrec::codigoArchivoNivel1++;
			nombre_archivo += "_N1_";
			cod_str << codigoArchivoNivel1;
		}
		nombre_archivo += cod_str.str();
		nombre_archivo += EXT_ARCHIVO;
	} else
		nombre_archivo += EXT_ARCHIVO;
	
	nombreArchivoDocFrecu = new char[nombre_archivo.size() + 1];
	strcpy(nombreArchivoDocFrecu, nombre_archivo.c_str());
	strcpy (this->modo, modo);
	archivo = NULL;
	//no necesito cantidad de registros, pues me manejo por referencias del indice parcial
	
}

// DESTRUCTOR
AlmacenamientoDocFrec::~AlmacenamientoDocFrec() {
	
	if (archivo != NULL) {
		fclose(archivo);
	}

	if (destroy)
		remove (nombreArchivoDocFrecu);

	delete [] nombreArchivoDocFrecu;
}

void AlmacenamientoDocFrec::abrirArchivo () {
	archivo = fopen(nombreArchivoDocFrecu, modo);
}

/* escribe la lista de doc-frec o la lista de doc-peso 
 * de acuerdo a lo se le indique */
tListaDocDistancias* AlmacenamientoDocFrec::leer(int offset, int frecuencia, bool conPeso) {
	
	int distActual, i, j;
	int frec = 0;
	float peso = 0;
	int distAnterior = 0;
	unsigned char byte;
	unsigned char cantBytes = 0;
	RegistroDistancias * regDist;
	
	tListaDocDistancias * lista = new tListaDocDistancias();
	tListaBytes codificacion;
	
	cGamma * gamma = new cGamma();

	fseek(archivo, offset, SEEK_SET);
	
	for(i = 0; i < frecuencia; i++)
	{		
		if (conPeso) {
			regDist = new RegistroDocPeso();
			fread((void*)&peso, sizeof(float), 1, archivo);
			((RegistroDocPeso *)regDist)->setPeso(peso);
		} else {
			regDist = new RegistroDocFrec();
			fread((void*)&frec, sizeof(int), 1, archivo);
			((RegistroDocFrec *)regDist)->setFrecuencia(frec);
		}
		
		fread((void*)&cantBytes, sizeof(unsigned char), 1, archivo);
		
		for(j = 0; j < cantBytes;j++)
		{
			fread((void*)&byte, sizeof(unsigned char), 1, archivo);
			codificacion.push_back(byte);
		}
		
		distActual = gamma->decodificacion(codificacion);
		
		// seteo del documento
		regDist->setDocumento(distActual + distAnterior);
		lista->push_back(regDist);
		distAnterior = distActual + distAnterior;
	}

	delete gamma;

	return lista;
}

/* escribe la lista de doc-frec o la lista de doc-peso 
 * de acuerdo a lo se le indique */
int AlmacenamientoDocFrec::escribir(tListaDocDistancias * lista, bool conPeso)
{
	int distancia, frecuencia;
	float peso = 0;
	int docAnterior = 0;
	int offset = 0;
	unsigned char cantBytes, byte;
	cGamma * gamma = new cGamma();
	tListaBytes * codificacion = NULL;
	tListaDocDistancias::iterator i;
	tListaBytes::iterator j;
	
	offset = ftell(archivo);
	
	for(i = lista->begin(); i != lista->end();i++)
	{
		if (conPeso) {
			peso = ((RegistroDocPeso *)(*i))->getPeso();
			fwrite((void*)&peso, sizeof(float), 1, archivo);
		} else {
			frecuencia = ((RegistroDocFrec *)(*i))->getFrecuencia();
			fwrite((void*)&frecuencia, sizeof(int), 1, archivo);
		}

		distancia = ((*i)->getDocumento()) - docAnterior;
		codificacion = gamma->codificacion(distancia);
		cantBytes = (unsigned char)codificacion->size();
		
		fwrite((void*)&cantBytes, sizeof(unsigned char), 1, archivo);
		
		for(j = codificacion->begin(); j != codificacion->end(); j++)
		{
			byte = (*j);
			fwrite((void*)&(byte), sizeof(unsigned char), 1, archivo);
		}
		
		docAnterior = (*i)->getDocumento();

		delete codificacion;
	}

	delete gamma;
	
	return offset;
}

void AlmacenamientoDocFrec::resetearCodigoArchivo() {
	AlmacenamientoDocFrec::codigoArchivo = 0;
}

void AlmacenamientoDocFrec::resetearCodigoArchivoNivel1() {
	AlmacenamientoDocFrec::codigoArchivoNivel1 = 0;
}

// setea la variable que indica si el archivo al cerrarse debe ser destruido
void AlmacenamientoDocFrec::setDestroy() {
	destroy = true;
}

// renombra el archivo con el nombre final (sin niveles)
void AlmacenamientoDocFrec::renombrarArchivoFinal () {
	
	string nombreArchivoFinal;

	nombreArchivoFinal = Entorno::Instance().getDataPath();
	nombreArchivoFinal += NOMBRE_ARCHIVO_DOCFREC;
	nombreArchivoFinal += EXT_ARCHIVO;
	
	rename (nombreArchivoDocFrecu, nombreArchivoFinal.c_str());
	delete [] nombreArchivoDocFrecu;
	nombreArchivoDocFrecu = new char[nombreArchivoFinal.size() + 1];
	strcpy(nombreArchivoDocFrecu, nombreArchivoFinal.c_str());
	////cout << "AlmacenamientoDocFrec.cpp,185: nombreArchivoDocFrecu: " << nombreArchivoDocFrecu << endl;
}

// renombra el archivo al nivel pasado
void AlmacenamientoDocFrec::renombrarArchivoANivel (char * nivel) {

	string nombreArchivoDocFrec;
	stringstream cod_str;

	if (strcmp (nivel, NIVEL_0) == 0) 
		cod_str << ++codigoArchivo;
	else
		cod_str << ++codigoArchivoNivel1;
	
	nombreArchivoDocFrec = Entorno::Instance().getDataPath();
	nombreArchivoDocFrec += NOMBRE_ARCHIVO_DOCFREC;
	nombreArchivoDocFrec += nivel;
	nombreArchivoDocFrec += cod_str.str();
	nombreArchivoDocFrec += EXT_ARCHIVO;

	rename (nombreArchivoDocFrecu, nombreArchivoDocFrec.c_str());
	nombreArchivoDocFrecu = new char[nombreArchivoDocFrec.size() + 1];
	strcpy(nombreArchivoDocFrecu, nombreArchivoDocFrec.c_str());
	////cout << "AlmacenamientoDocFrec.cpp,208: nombreArchivoDocFrecu: " << nombreArchivoDocFrecu << endl;
}
